<?php

namespace app\models\tableModel;

use app\models\CommonModel;
use app\models\table\AdminAuth;
use yii\db\Exception;
use yii\helpers\ArrayHelper;

/**
 * 后台权限[Model]
 * User: Administrator
 * Date: 1995/12/22
 * Time: 17:59
 */
class AdminAuthModel extends AdminAuth
{

    /**
     * 状态 列表
     * @var array
     */
    private static $statusList = [
        'disabled' => -1,
        'open' => 1
    ];
    /**
     * 状态文本 列表
     * @var array
     */
    private static $statusListText = [
        -1 => '已禁用',
        1 => '已开启'
    ];
    /**
     * 排序最大值
     * @var int
     */
    protected static $sortMax = 9999999;
    /**
     * 排序最小值
     * @var int
     */
    protected static $sortMin = -999999;
    /**
     * 不允许检测权限列表
     *  - 需配合[config]中的[modules]使用，[params][loadInAuth]是[false]的模块直接被舍弃
     * @var array
     */
    protected static $filterList = [
        'wii' => '*',
        'gii' => '*',
        'debug' => '*',
        'any' => [ // 匹配全部[modules]
            'any' => [ // 匹配全部[controller]
                'behaviors',
                'beforeAction',
                '__construct',
                'init',
                'afterAction',
                's' // 对应[actions]方法，由于正则只能写成`s`
            ]
        ]
    ];
    /**
     * 权限[modules]列表
     * @var array
     */
    protected $moduleList = [];
    /**
     * 权限[controller]列表
     * @var array
     */
    protected $controllerList = [];
    /**
     * 权限[action]列表
     * @var array
     */
    protected $actionList = [];
    /**
     * 从数据库获取的[控制器]信息
     * @var array
     */
    protected $dbControlList = [];
    /**
     * 是否可以更新权限|TODO：小心你的重命名就没了
     * ps.开启此变量，则会以文件为准重新更新权限表中 name, content 字段
     * @var bool
     */
    protected $openUpdate = true;
    /**
     * 基础[SQL]
     * @var \yii\db\ActiveQuery
     */
    public $sqlBase;

    /**
     * 静态错误暂存
     * @var
     */
    public static $error_;

    /**
     * 条件
     * @var
     */
    public $where;

    /**
     * 规则验证
     * @return array
     */
    public function rules()
    {

        $parent = parent::rules();
        // 状态
        $statusList = array_values(self::getStatList());

        return ArrayHelper::merge($parent, [
            ['status', 'in', 'range' => $statusList, 'message' => '状态不合法'],
            ['parent_id', 'checkParentId'],
        ]);
    }

    /**
     * 重写label的 文字
     */
    public function attributeLabels()
    {
        return [
            'id' => '编号',
            'name' => '名称',
            'status' => '状态',
            'sort' => '排序',
        ];
    }

    /**
     * 场景
     * @return array
     */
    public function scenarios()
    {

        $scenarios = parent::scenarios();
        return ArrayHelper::merge($scenarios, [
            [
                // 自定义场景 (无用请删除)
                'scUpdate' => [
                    'someAttributes'
                ]
            ]
        ]);
    }

    /**
     * 加载整体[Model]
     * @param null $id 编号
     * @param string $scenario 场景
     * @return AdminAuthModel|\yii\db\ActiveQuery|null
     */
    public static function loadModel($id = null, $scenario = 'default')
    {

        // 实力化类
        $model = new self();

        // [id]是[true]返回
        if ($id === true) { // 无法加载场景直接返回

            return $model::find();
        } else if (!empty($id)) {  // 有[id]去查询

            $model = $model::findOne($id);
        }

        // 条目不存在或者需要[find]都返回 - 无法加载场景
        if (!$model || $id === true) {
            return $model;
        }

        // 场景
        $sceList = array_keys($model->scenarios());
        if (!empty($scenario) && in_array($scenario, $sceList)) $model->setScenario($scenario);

        return $model;
    }

    /**
     * 验证[parent_id]是否合法
     * @param null $attributeNames
     * @return bool
     */
    public function checkParentId($attributeNames = null)
    {

        // [0]组委父级菜单直接成功
        if ($this->$attributeNames == '0') {
            return true;
        }

        $parentItem = self::findOne([
            'id' => $this->$attributeNames
        ]);
        // 记录不存在
        if (empty($parentItem)) {
            $this->addError($attributeNames, '父级菜单不存在');
            return false;
        }
        // 记录不存在
        if ($parentItem['status'] == self::getStatDisabled()) {
            $this->addError($attributeNames, '父级菜单已禁用');
            return false;
        }

        // 如果父级编号是空的 证明无上级
        if (empty($parentItem['parent_id'])) return true;

        // 否则。在向上一级找到顶级
        $parentItem = self::findOne([
            'id' => $parentItem['parent_id']
        ]);

        // 顶级记录不存在
        if (empty($parentItem)) {
            $this->addError($attributeNames, '顶级菜单不存在');
            return false;
        }
        // 顶级记录不存在
        if ($parentItem['status'] == self::getStatDisabled()) {
            $this->addError($attributeNames, '顶级菜单已禁用');
            return false;
        }

        return true;
    }

    /**
     * 获取全部列表
     * @param null $filed
     * @param array $condition
     * @return array|\yii\db\ActiveRecord[]
     */
    public function getList($filed = null, $condition = [])
    {

        // 条件
        $where = $this->where;

        // 查找的 字段空的 就默认给列表
        if (!$filed) $filed = '*';

        // 基础 where加载完毕
        $this->sqlBase = $this::find()
            ->select($filed);

        // 可以加载全局条件
        if (empty($condition)) $this->sqlBase = $this->sqlBase->where($where);

        // 数据的获取 分页等
        $list = $this->sqlBase
            ->andWhere($condition)
            ->orderBy("`sort` DESC, `id` DESC")
            ->asArray()->all();

        // 格式化数据
        foreach ($list as $k => &$v) {

            // 状态文本
            if (isset($v['status'])) {
                $v['status_text'] = self::getStatusText($v['status']);
            }

            // 一些自定义属性给前端
            $v['isParent'] = true;

            // 如果禁用
            if (isset($v['status']) && $v['status'] == self::getStatDisabled()) {
                $v['chkDisabled'] = true;
            } else {
                $v['chkDisabled'] = false;
            }

            $v['children'] = $this->getChildList($v['id']);
            // 取出子集全部的 是否禁用
            $stage = array_unique(array_column($v['children'], 'chkDisabled'));
            // 如果没有[否]则代表 父级也可以禁用
            if ($v['chkDisabled'] == false && in_array(false, $stage)) {
                $v['chkDisabled'] = false;
            } else {
                $v['chkDisabled'] = true;
            }
        }

        return $list;
    }

    /**
     * 获取[二级菜单]全部列表
     * @param $parentId
     * @return array|\yii\db\ActiveRecord[]
     */
    public function getChildList($parentId = 0)
    {

        if (empty($parentId)) return [];

        // 数据的获取 分页等
        $list = $this->sqlBase->where($this->where)->andWhere([
            'parent_id' => $parentId
        ])->orderBy("`sort` DESC, `id` DESC")->asArray()->all();

        // 格式化数据
        foreach ($list as $k => &$sv) {

            // 状态文本
            if (isset($sv['status'])) {
                $sv['status_text'] = self::getStatusText($sv['status']);
            }

            // 如果禁用
            if (isset($sv['status']) && $sv['status'] == self::getStatDisabled()) {
                $sv['chkDisabled'] = true;
            } else {
                $sv['chkDisabled'] = false;
            }
        }

        return $list;
    }

    /**
     * 加载条件
     * @param $where
     * @return $this
     */
    public function loadWhere($where)
    {

        // 条件不存在
        if (empty($where)) {

            return $this;
        }

        // 如果[where][0]是'and' 直接赋值
        $canRetList = ['and', 'or', 'AND', 'OR'];
        if (!empty($where[0]) && in_array($where[0], $canRetList)) {

            $this->where = $where;
            return $this;
        }

        // 不是数组 字符直接 判断
        if (!is_array($where)) {

            // 条件是 有效
            if (!empty($where) && $this->hasAttribute($where))

                $this->where = $where;

            // 条件 无有效
            return $this;
        }

        // 循环  条件是否有效
        $stagingWhere = ['and'];
        foreach ($where as $k => $v) {

            // 首先值是有的，不能是空
            if ($v && is_array($v) && count($v) > 0 && $this->hasAttribute($k)) {

                $stagingWhere[] = ['IN', $k, array_values($v)];
                continue;
            }

            // 首先值是有的，不能是空
            if ($v && $this->hasAttribute($k))

                $stagingWhere[] = ['=', $k, $v];
        }

        // 条件最终赋值
        $this->where = $stagingWhere;

        return $this;
    }

    /**
     * 获取记录总数量
     * @return int|string
     */
    public function getCount()
    {

        // 条件
        $where = $this->where;

        // 基础 where加载完毕
        $count = $this::find()->where($where)->count();

        return $count;
    }

    /**
     * 添加|保存
     * @return bool
     */
    public function saveData()
    {

        $nowTime = time();
        // 添加的话要赋值一些初始数据
        if (empty($this->id)) {

            // 可以是走mongoId
            $this->id = CommonModel::newMongoId();
            $this->add_time = $nowTime;
        }
        // 父级编号默认赋值空则为 '0'
        if (empty($this->parent_id)) {
            $this->parent_id = '0';
        }

        if ($this->hasErrors() || !$this->validate() || !$this->save()) {

            return false;
        }

        return true;
    }

    /**
     * 加载[模块]
     * @return $this
     */
    public function loadModules()
    {

        // modules 列表
        $moduleList = \Yii::$app->modules;

        if (empty($moduleList)) {
            $this->addError('modules error', '无法加载[modules]列表');
            return $this;
        }

        foreach ($moduleList as $mk => $mv) {


            // 获取此模块下控制器路径
            $moduleInfo = \Yii::$app->getModule($mk);

            // 如果[module]是排除的列表，就不添加
            if (!property_exists($moduleInfo, 'params')) continue;
            if (empty($moduleInfo->params['loadInAuth'])) continue;

            // 获取此模块下控制器路径
            $controllerPath = $moduleInfo->getControllerPath();

            // 文件打不开无权限返回空
            if (!opendir($controllerPath)) {
                $this->addError('controller error',
                    '文件夹[' . $controllerPath . ']暂无读取权限');
                return $this;
            }

            // 获取下当前[模块下]下[控制器]是否可以检测权限
            if (!self::checkModule($mk)) continue;

            // 权限[modules]列表 暂存
            $this->moduleList[] = [
                'name' => $mk,
                'controllerPath' => $controllerPath
            ];
        }

        // 信息返回下
        return $this;
    }

    /**
     * 加载[控制器]
     * @return $this
     */
    public function loadControllers()
    {

        if (empty($this->moduleList)) {
            $this->addError('modules error', '[controllers]列表无法加载控制器。');
            return $this;
        }

        $conList = [];
        foreach ($this->moduleList as $mk => $mv) {

            // 存储格式化后的[controller]名称，example: GetNameController.php 得到 get-name
            $conRouteName = '';
            // 获取模块控制器路径下所有合法控制器
            $file = glob($mv['controllerPath'] . '/*Controller.php');
            foreach ($file as $kf => $vf) {

                if (empty($vf) || !file_exists($vf)) continue;
                // 格式化后的[controller]名称
                $conRouteName = substr(preg_replace('/Controller/', '', $vf),
                    0, -4);

                // 路径信息
                $pathInfo = pathinfo($conRouteName);
                // 文件基础名不存在返回下
                if (empty($pathInfo['basename'])) continue;

                // 检测当前[controller]是否在[modules]禁止[controller]下
                if (!self::checkController($mk, $conRouteName)) continue;

                // 命名空间
                $nameSpace = self::getNameSpace($vf);

                // 路由
                $route = "/{$this->formatRoute($mv['name'])}/{$this->formatRoute($pathInfo['basename'])}";
                $controllerNote = $this->getControllerNote($vf);
                // 暂存到成员变量数组
                $this->controllerList[] = [
                    'name' => $controllerNote, // 注释
                    'content' => $controllerNote, // 注释
                    'module' => $mv['name'], // example: v1
                    'controller' => $pathInfo['basename'], // example: AdminAuthRole
                    'nameSpace' => $nameSpace,
                    'path' => $vf, // example:D:/v1/TestController.php - 具体目录
                    'route' => $route
                ];
            }
        }

        // 信息返回下
        return $this;
    }

    /**
     * 加载[方法]
     * @return $this
     */
    public function loadActions()
    {

        if (empty($this->controllerList)) {
            $this->addError('controllers error', '[控制器]列表我空，无法加载方法。');
            return $this;
        }

        // 合法[action]匹配规则
        $actionPre = '/public function action(.*?)\(/';
        foreach ($this->controllerList as $k => $v) {

            // 打开[controller]文件
            $handle = fopen($v['path'], "r");

            // 打不开
            if (!$handle) continue;

            // 说明暂存
            $explain = '';
            // 当前行
            $line = '';
            // 上一行
            $lastLine = '';
            // 获取controller内容
            while (($line = fgets($handle)) !== false) {

                // 匹配下上一行，如果是有 /** 或者 /* 字符串 */| 注释内容！！！注意格式不符合自行修改
                if (preg_match('/\/\*+/', $lastLine)) {

                    // 匹配下 * XXXX
                    preg_match('/\*+[\s]+([\s\S]*)/', $line, $explain);

                    // 可能会留有一个换行
                    $explain = preg_replace('/[\r\n]/', '', $explain);

                    // 说明
                    $explain = $explain[1];
                }

                // 复制下上一行内容赋值 | 主要给注释说明用
                $lastLine = $line;

                // 根据每行的[function]分隔|！！！注意格式不符合自行修改
                if (!preg_match($actionPre, $line, $action)) continue;
                // 方法名称太少也不行 哈哈哈
                if (strlen($action[1]) <= 0) continue;

                // 方法
                $action = $action[1];

                // 检测当前[action]是否在合法下
                if (!self::checkAction($v['module'], $v['controller'], $action)) continue;

                $route = "/{$this->formatRoute($v['module'])}/{$this->formatRoute($v['controller'])}/{$this->formatRoute($action)}";

                $actionStage = [
                    'name' => $explain,
                    'parent_id' => 0,
                    'content' => $explain,
                    'module' => $v['module'],
                    'controller' => $v['controller'],
                    'controllerRoute' => $v['route'], // 控制器的路由
                    'action' => $action,
                    'url' => !self::getUrlForRoute($route) ? $route : self::getUrlForRoute($route),
                    'route' => $route
                ];
                // 赋值最终数据
                $this->actionList[] = $actionStage;
                $this->controllerList[$k]['children'][] = $actionStage;

            }
        }

        // 信息返回下
        return $this;
    }

    /**
     * 更新[控制器]权限信息
     */
    public function updateController()
    {

        // 检测已获取的控制器列表 是否合法
        if (empty($this->controllerList) || count($this->controllerList) < 1) {
            $this->addError('controllerList', '控制器列表未加载，无法进行操作');
            return false;
        }

        // 取出路由
        $routeList = array_column($this->controllerList, 'route');
        // 查询数据库 根据路由来确定条目
        $this->dbControlList = self::find()
            ->select('
                id,
                name,
                content,
                route,
                parent_id
            ')->where(['route' => $routeList])
            ->asArray()->all();
        // 格式化数据库数据 以[route]做键
        $routeDbList = array_column($this->dbControlList, null, 'route');
        // 取出数据库数据[route]列表
        $routeDbRouteList = array_column($routeDbList, 'route');

        // ********** 1、操作差集 - 新添加 **********
        // 需要添加 - 取出路由差集
        $haveNotRoute = array_diff($routeList, $routeDbRouteList);
        // 以[route]作为键 控制器列表
        $controllerList = array_column($this->controllerList, null, 'route');

        $createData = []; // 即将要添加的数据列表
        $selfModel = new self(); // 初始化个自己做下验证用
        $nowTime = time(); // 当前时间
        $stage = []; // 暂存数据
        foreach ($haveNotRoute as $k => $v) {

            if (empty($controllerList[$v])) {
                $this->addError('controllerList', "在控制器列表中找不到[{$v}]");
                return false;
            }
            // 字段数据
            $stage = [
                'id' => CommonModel::newMongoId(),
                'name' => $controllerList[$v]['name'],
                'parent_id' => '0', // 控制器上级编号为 0
                'content' => $controllerList[$v]['name'],
                'add_time' => $nowTime,
                'module' => $controllerList[$v]['module'],
                'controller' => $controllerList[$v]['controller'],
                'action' => '', // 控制器级别 方法名为空
                'route' => $controllerList[$v]['route'],
                'status' => self::getStatOpen(),
                'url' => '' // 控制器级别 连接为空
            ];

            // 验证下数据是否合法
            $selfModel->setAttributes($stage);
            if (!$selfModel->validate()) {
                $error = CommonModel::getModelError($selfModel->errors);
                $this->addError(500, '[控制器]验证权限：' . $error['msg']);
                return false;
            }

            $createData[] = $stage;
        }

        // 即将添加数据 大于[1]条并且添加失败
        if (count($createData) > 0 && !$this->createData($createData)) {
            $this->addError('function addData error', '添加控制器权限失败，请查看日志');
            return false;
        }

        // ********** 2、操作交集 - 更新 **********
        // 需要更新 - 取出路由交集
        $haveRoute = array_intersect($routeList, $routeDbRouteList);

        $updateData = []; // 即将要更新的数据列表
        $dbStage = []; // 数据库条目数据暂存 - 比对用
        foreach ($haveRoute as $k => $v) {
            if (empty($controllerList[$v])) {
                $this->addError('controllerList', "在[控制器]列表中找不到[{$v}]");
                return false;
            }

            $dbStage = [
                'name' => $routeDbList[$v]['name'],
                'content' => $routeDbList[$v]['content']
            ];
            // 字段数据 - 其实能更新的基本也就是只有[注释]了
            $stage = [
                'name' => $controllerList[$v]['name'],
                'content' => $controllerList[$v]['content']
            ];

            // 先检测下 - 无任何变化下一条
            if (empty($routeDbList[$v]) || $dbStage == $stage) continue;

            // 更新失败
            $condition = ['id' => $routeDbList[$v]['id']];
            if ($this->openUpdate && !$selfModel::updateField($condition, $stage)) {
                $this->addError(500, '[控制器]权限更新失败，请查看日志');
                return false;
            }

            $updateData[] = $stage;
        }

        return true;
    }

    /**
     * 更新[方法]权限信息
     */
    public function updateAction()
    {

        // 检测已获取的控制器列表 是否合法
        if (empty($this->controllerList) || count($this->controllerList) < 1) {
            $this->addError('controllerList', '控制器列表未加载，无法进行操作');
            return false;
        }

        // ********** 1、取出[控制器]格式化数据 **********
        // 数据库控制器列表 不存在
        if (empty($this->dbControlList)) {

            // 查询数据库 根据路由来确定条目
            $this->dbControlList = self::find()
                ->select('
                    id,
                    name,
                    content,
                    route,
                    parent_id
                ')->where([
                    'route' => array_column($this->controllerList, 'route')
                ])->asArray()->all();
        }
        // 格式化数据库数据 以[route]做键
        $routeDbList = array_column($this->dbControlList, null, 'route');


        // ********** 1、取出[方法]格式化数据 **********
        // 取出路由
        $stage = array_column($this->controllerList, 'children');
        $actionList = [];
        foreach ($stage as $k => $v) {
            $actionList = array_merge($actionList, $v);
        }
        // 取出数据库[方法]列表
        $dbActionList = self::find()
            ->select('
                id,
                name,
                content,
                route,
                parent_id,
                url
            ')->where(['route' => array_column($actionList, 'route')])
            ->asArray()->all();
        // 格式化列表以[route]为键
        $dbActionList = array_column($dbActionList, null, 'route');
        // 取出[route]列表
        $routeDbRouteList = array_column($dbActionList, 'route');


        // ********** 1、操作差集 - 新添加 **********
        // 格式化数据库数据 以[route]做键
        $routeList = array_column($this->actionList, 'route');
        // 需要添加 - 取出路由差集
        $haveNotRoute = array_diff($routeList, $routeDbRouteList);

        // 以[route]作为键 方法列表
        $actionList = array_column($this->actionList, null, 'route');

        $createData = []; // 即将要添加的数据列表
        $selfModel = new self(); // 初始化个自己做下验证用
        $nowTime = time(); // 当前时间
        $stage = []; // 暂存数据
        foreach ($haveNotRoute as $k => $v) {
            // 方法列表 方法找不到
            if (empty($actionList[$v])) {
                $this->addError('controllerList', "在[文件]方法列表中找不到[{$v}]");
                return false;
            }
            // 数据库控制器列表 控制器找不到
            if (empty($routeDbList[$actionList[$v]['controllerRoute']])) {
                $this->addError('controllerList', "在[数据存储]控制器列表中找不到[{$v}]");
                return false;
            }
            // 字段数据
            $stage = [
                'id' => CommonModel::newMongoId(),
                'name' => $actionList[$v]['name'],
                'parent_id' => $routeDbList[$actionList[$v]['controllerRoute']]['id'],
                'content' => $actionList[$v]['name'],
                'add_time' => $nowTime,
                'module' => $actionList[$v]['module'],
                'controller' => $actionList[$v]['controller'],
                'action' => $actionList[$v]['action'],
                'route' => $actionList[$v]['route'],
                'status' => self::getStatOpen(),
                'url' => $actionList[$v]['url']
            ];

            // 验证下数据是否合法
            $selfModel->setAttributes($stage);
            if (!$selfModel->validate()) {
                $error = CommonModel::getModelError($selfModel->errors);
                $this->addError(500, '[方法]验证权限：' . $error['msg']);
                return false;
            }

            $createData[] = $stage;
        }

        // 即将添加数据 大于[1]条并且添加失败
        if (count($createData) > 0 && !$this->createData($createData)) {
            $this->addError('function addData error', '添加[方法]权限失败，请查看日志');
            return false;
        }

        // ********** 2、操作交集 - 更新 **********
        // 需要更新 - 取出路由交集
        $haveRoute = array_intersect($routeList, $routeDbRouteList);

        $updateData = []; // 即将要更新的数据列表
        $dbStage = []; // 数据库条目数据暂存 - 比对用
        foreach ($haveRoute as $k => $v) {

            if (empty($actionList[$v])) {

                $this->addError('controllerList', "在[方法]列表中找不到[{$v}]");
                return false;
            }

            // 开启更新
            if ($this->openUpdate) {

                $dbStage = [
                    // 'name' => $dbActionList[$v]['name'],
                    'content' => $dbActionList[$v]['content'],
                    'url' => $dbActionList[$v]['url']
                ];
                $stage = [
                    // 'name' => $actionList[$v]['name'],
                    'content' => $actionList[$v]['content'],
                    'url' => $actionList[$v]['url']
                ];
            } else { // 未开启更新 - 只刷新规定数据 | TODO:暂不更新名称了，因为有个重命名，更新名称会覆盖掉

                $dbStage = [
                    'url' => $dbActionList[$v]['url']
                ];
                $stage = [
                    'url' => $actionList[$v]['url']
                ];
            }

            // 先检测下 - 无任何变化下一条
            if (empty($dbActionList[$v]) || $dbStage == $stage) continue;

            // 更新失败
            $condition = ['id' => $dbActionList[$v]['id']];
            if ($this->openUpdate && !$selfModel::updateField($condition, $stage)) {
                $this->addError(500, '[方法]权限更新失败，请查看日志');
                return false;
            }

            $updateData[] = $stage;
        }

        return true;
    }

    /**
     * controller | action 名称重组为路由模式
     * @param $str
     * @return string
     */
    private function formatRoute($str)
    {
        // 根据 打蝎子父母分割成数组
        $array = preg_split("/(?=[A-Z])/", $str);

        // 数组去空
        $array = array_filter($array);

        // 根据 - 组合
        $array = implode($array, '-');

        // 字符串大写字母 -> 小写字母
        $array = strtolower($array);

        return $array;
    }

    /**
     * 获取[controller]文件的注释 - [controller]注释
     * @param $path
     * @return null
     */
    public function getControllerNote($path)
    {

        // 打开controller 文件
        $handle = fopen($path, 'r');

        // 打开成功
        if (!$handle) return null;

        $lastLine = '';
        $explain = '';
        // 获取controller内容
        while (($line = fgets($handle)) !== false) {

            // 匹配下上一行，如果是有 /** 或者 /* 字符串 */| 注释内容！！！注意格式不符合自行修改
            if (preg_match('/\/\*+/', $lastLine)) {

                // 匹配下 * XXXX
                preg_match('/\*+[\s]+([\s\S]*)/', $line, $explain);

                // 可能会留有一个换行
                $explain = preg_replace('/[\r\n]/', '', $explain);

                return $explain[1];
            }

            // 复制下上一行内容赋值 | 主要给注释说明用
            $lastLine = $line;
        }

        return null;
    }




    /**
     * 批量更新某些字段|ps.请事先做好字段数据校验
     * @param $condition
     * @param array $fieldVal
     * @return bool
     */
    public static function updateField($condition, $fieldVal = [])
    {

        $model = new self();
        foreach ($fieldVal as $k => $v) {

            if (!$model->hasAttribute($k)) {

                unset($fieldVal[$k]);
                continue;
            }
        }

        $db = \Yii::$app->db->createCommand();

        try {

            $db->update(self::tableName(), $fieldVal, $condition)->execute();

            // 否则成功
            return true;
        } catch (Exception $error) {

            // 记录下错误日志
            \Yii::error([

                "````````````````````````````````````````````````````````",
                "``                      数据库错误                      ``",
                "`` 错误详情: 修改[指定字段]失败，{$error->getMessage()}      ``",
                "`` SQL语句: {$db->getRawSql()}                         ``",
                "`` 错误信息和参数详情:                                    ``",
                "````````````````````````````````````````````````````````",
                $error->getTraceAsString()
            ], 'normal');

            self::$error_ = empty($error->errorInfo) ?
                $error->getMessage() :
                implode(' | ', $error->errorInfo);

            return false;
        }
    }

    /**
     * 批量添加数据|ps.请事先做好字段数据校验
     * @param array $createData
     * @return bool
     */
    public static function createData($createData = [])
    {

        $db = \Yii::$app->db->createCommand();
        try {

            // 还行写入多条
            $addResult = $db->batchInsert(self::tableName(),
                [
                    'id',
                    'name',
                    'parent_id',
                    'content',
                    'add_time',
                    'module',
                    'controller',
                    'action',
                    'route',
                    'status',
                    'url'
                ], $createData
            )->execute();

            return true;
        } catch (Exception $error) {

            // 记录下错误日志
            \Yii::error([

                "````````````````````````````````````````````````````````",
                "``                      数据库错误                      ``",
                "`` 错误详情: [后台权限]批量添加[新控制器权限]失败             ``",
                "``          {$error->getMessage()}                      ``",
                "`` SQL语句: {$db->getRawSql()}                         ``",
                "`` 错误信息和参数详情:                                   ``",
                "````````````````````````````````````````````````````````",
                $error->getTraceAsString()
            ], 'normal');

            return false;
        }
    }

    /**
     * 检测[模块]是否允许不获取权限
     * @param string $module 模块
     * @return bool |true: 可以进行加载权限，false：不可加载权限
     */
    public static function checkModule($module = '')
    {

        $filterList = self::$filterList;
        // 没有模块 则可以加载
        if (!empty($filterList[$module])) {

            // 获取下模块下控制器列表
            $controllerList = $filterList[$module];

            // 如果模块下控制器不是通配符 则可以加载
            if ($controllerList == '*' || in_array('*', array_keys($controllerList))) {
                return false;
            }
        }

        // 检测[任何]下 禁止控制器 不通过
        if (!empty($filterList['any'])) {

            // 获取下模块下控制器下所有方法
            $controllerList = $filterList['any'];
            // 方法列表有通配符 则不通过
            if ($controllerList == '*' || in_array('*', array_keys($controllerList))) {
                return false;

            }
        }

        // 否则不可加载
        return true;
    }

    /**
     * 检测[控制器]是否允许不获取权限
     * @param string $module 模块
     * @param string $controller 控制器
     * @return bool |true: 可以进行加载权限，false：不可加载权限
     */
    public static function checkController($module = '', $controller = '')
    {

        // 先调用模块检测 - 不通过 则直接返回不通过
        if (!self::checkModule($module)) return false;

        $filterList = self::$filterList;
        // 如果控制器处于 禁止控制器中
        if (!empty($filterList[$module][$controller])) {
            // 获取下模块下控制器下所有方法
            $actionList = $filterList[$module][$controller];

            // 方法列表有通配符 则不通过
            if ($actionList == '*' || in_array('*', array_values($actionList))) {
                return false;
            }
        }

        // 检测[任何]下 禁止控制器 不通过
        if (!empty($filterList['any'][$controller])) {

            // 获取下模块下控制器下所有方法[any][控制器名]
            $actionList = $filterList['any'][$controller];
            // 方法列表有通配符 则不通过
            if ($actionList == '*' || in_array('*', array_values($actionList))) {
                return false;
            }
        }

        return true;
    }

    /**
     * 检测[方法]是否允许不获取权限
     * @param string $module 模块
     * @param string $controller 控制器
     * @param string $action 方法
     * @return bool |true: 可以进行加载权限，false：不可加载权限
     */
    public static function checkAction($module = '', $controller = '', $action = '')
    {

        // 先调用模块检测 - 不通过 则直接返回不通过
        if (!self::checkModule($module)) return false;

        // 先调用控制器检测 - 不通过 则直接返回不通过
        if (!self::checkController($module, $controller)) return false;

        $filterList = self::$filterList;
        // 检测了模块、控制器，方法空的 不通过
        if (!empty($filterList[$module][$controller])) {
            $actionList = array_values($filterList[$module][$controller]);
            if (in_array($action, $actionList)) return false;
        }
        // 检测了任何模块、控制器 不通过
        if (!empty($filterList['any'][$controller])) {
            $actionList = array_values($filterList['any'][$controller]);
            if (in_array($action, $actionList)) return false;
        }
        // 检测了任何模块、任何控制器 不通过
        if (!empty($filterList['any']['any'])) {
            $actionList = array_values($filterList['any']['any']);
            if (in_array($action, $actionList)) return false;
        }
        return true;
    }

    /**
     * 根据[路由]获取连接
     * @param string $route
     * @return bool
     */
    private static function getUrlForRoute($route = '')
    {

        $config = \Yii::$app->getComponents();
        if (empty($config['urlManager']['rules'])) return false;
        $urlList = array_flip($config['urlManager']['rules']);
        if (empty($urlList[$route])) return false;
        return $urlList[$route];
    }

    /**
     * 获取命名空间
     * @param $file
     * @return mixed|string
     */
    private static function getNameSpace($file)
    {

        // ex: /var/www/html/modules/html/controllers/AdminAuthController.php

        $projectPath = \Yii::getAlias('@app');
        // /modules/html/controllers/AdminAuthController.php
        $stage = str_replace($projectPath, '', $file);
        // /modules/html/controllers/AdminAuthController
        $stage = str_replace('.php', '', $stage);
        // app/modules/html/controllers/AdminAuthController
        $stage = 'app' . $stage;
        // app\modules\html\controllers\AdminAuthController
        $stage = str_replace('/', '\\', $stage);
        return $stage;
    }


    /**
     * 返回排序最大值
     * @return int
     */
    public static function getSortMax()
    {
        return self::$sortMax;
    }

    /**
     * 返回排序最小值
     * @return int
     */
    public static function getSortMin()
    {
        return self::$sortMin;
    }

    /**
     * 获取[正常]状态 值
     * @return mixed|string
     */
    public static function getStatOpen()
    {

        // 最终正常返回
        return self::$statusList['open'];
    }

    /**
     * 获取[禁用]状态 值
     * @return mixed|string
     */
    public static function getStatDisabled()
    {

        // 最终正常返回
        return self::$statusList['disabled'];
    }

    /**
     * 获取[状态]列表 值
     * @return mixed|string
     */
    public static function getStatList()
    {

        // 最终正常返回
        return self::$statusList;
    }

    /**
     * 获取[状态]文本
     * @param $value
     * @return mixed|string
     */
    public static function getStatusText($value)
    {

        // 列表
        $list = self::$statusListText;
        // 不合法 - 不存在
        if (empty($list[$value]))

            return '--';

        // 最终正常返回
        return $list[$value];
    }
}
